package com.nioserver.linklayer;

import com.nioserver.io.WorkPool;
import com.nioserver.io.task.ReadTask;
import com.nioserver.io.task.WriteTask;
import com.nioserver.lib.IMessageReader;
import com.nioserver.io.MessageWriter;
import com.nioserver.propertys.RoleMaps;
import org.luaj.vm2.LuaValue;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

/**
 * Created by jjenkov on 16-10-2015.
 */
public class Socket {

    public int socketId;

    public SocketChannel  socketChannel = null;
    public IMessageReader messageReader = null;
    public MessageWriter messageWriter = null;

    public boolean endOfStreamReached = false;


    private LuaValue luaOp;

    public LuaValue callLua(String command, String arg){
        LuaValue value = luaOp.get(command).call(arg);
        return value;
    }
    public ByteBuffer writeBuffer;
    public ByteBuffer readBuffer;
    public Socket() {
    }

    public Socket(SocketChannel socketChannel) {
        this.socketChannel = socketChannel;
//        luaOp = CallLua.getInstance().launch("core/handler");
    }

    public void readAsync(ByteBuffer byteBuffer)  {
        this.readBuffer = byteBuffer;
        WorkPool.getInstance().submitTask(new ReadTask(this));
    }
    public void writeAsync(ByteBuffer byteBuffer){

        this.writeBuffer = byteBuffer;
        WorkPool.getInstance().submitTask(new WriteTask(this));
    }
    public int read(ByteBuffer byteBuffer) throws IOException {
        if(this.socketChannel.isConnected() == false){
            this.socketChannel.close();
            return -1;
        }
        int bytesRead = 0;
        int totalBytesRead =0;
        try {
            bytesRead = this.socketChannel.read(byteBuffer);
            totalBytesRead = bytesRead;
            while(bytesRead > 0){
                bytesRead = this.socketChannel.read(byteBuffer);
                totalBytesRead += bytesRead;
            }
        }catch (Exception e){
            socketChannel.socket().close();
            socketChannel.close();
            shutDown();
        }
        if(bytesRead == -1){
            this.endOfStreamReached = true;
        }
        return totalBytesRead;
    }

    public void writeAsync(com.google.protobuf.GeneratedMessageV3 msg){
        writeAsync(ByteBuffer.wrap(msg.toByteArray()));
    }


    public int write(ByteBuffer byteBuffer) throws IOException{

        int totalBytesWritten = 0;
        try{
            int bytesWritten      = this.socketChannel.write(byteBuffer);
            totalBytesWritten = bytesWritten;
            while(bytesWritten > 0 && byteBuffer.hasRemaining()){
                bytesWritten = this.socketChannel.write(byteBuffer);
                totalBytesWritten += bytesWritten;
            }

        }catch (Exception e){

            this.socketChannel.close();
            shutDown();
        }
        return totalBytesWritten;
    }

    private void shutDown(){
        RoleMaps.deRole((int)socketId);
    }
}
